package com.hooper.order.service.impl;

import com.github.wxpay.sdk.WXPayConstants;
import com.github.wxpay.sdk.WXPayUtil;
import com.hooper.hosp.enums.PaymentStatusEnum;
import com.hooper.hosp.enums.PaymentTypeEnum;
import com.hooper.hosp.enums.RefundStatusEnum;
import com.hooper.hosp.model.order.OrderInfo;
import com.hooper.hosp.model.order.PaymentInfo;
import com.hooper.hosp.model.order.RefundInfo;
import com.hooper.hospital.common.exception.HospitalException;
import com.hooper.hospital.common.result.ResultCodeEnum;
import com.hooper.order.constant.WxProperties;
import com.hooper.order.service.OrderService;
import com.hooper.order.service.PaymentService;
import com.hooper.order.service.RefundInfoService;
import com.hooper.order.service.WeixinService;
import com.hooper.order.utils.HttpClient;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author Tim Hooper
 * @version 1.0
 * @time 2022/10/11/21:36
 */
@Service
@Slf4j
public class WeixinServiceImpl implements WeixinService {

    @Resource
    private OrderService orderService;
    @Resource
    private PaymentService paymentService;
    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Resource
    private RefundInfoService refundInfoService;

    @Resource
    private WxProperties wxProperties;

    @Override
    @Transactional(rollbackFor = Exception.class)
    @SuppressWarnings("unchecked")
    public Map<String, Object> createNative(String orderId) {
        try {
            Map<String, Object> res = (Map<String, Object>) redisTemplate.opsForValue().get(orderId);
            if (res != null) {
                return res;
            }
            //获取订单信息
            OrderInfo order = orderService.getById(orderId);
            if (order == null) {
                throw new HospitalException(ResultCodeEnum.ORDER_DATA_ERROR);
            }
            //将信息存入支付订单表中
            paymentService.savePayInfo(order, PaymentTypeEnum.WEIXIN.getStatus());
            //设置参数,调用微信api生成二维码
            //把参数转化为xml格式使用商户key加密
            Map paramMap = new HashMap();
            paramMap.put("appid", wxProperties.getAppid());
            paramMap.put("mch_id", wxProperties.getPartner());
            paramMap.put("nonce_str", WXPayUtil.generateNonceStr());
            String body = order.getReserveDate() + "就诊" + order.getDepname();
            paramMap.put("body", body);
            paramMap.put("out_trade_no", order.getOutTradeNo());
            //paramMap.put("total_fee", order.getAmount().multiply(new BigDecimal("100")).longValue()+"");
            paramMap.put("total_fee", "1");
            paramMap.put("spbill_create_ip", "127.0.0.1");
            paramMap.put("notify_url", "http://guli.shop/api/order/weixinPay/weixinNotify");
            paramMap.put("trade_type", "NATIVE");
            //2、HTTPClient来根据URL访问第三方接口并且传递参数
            HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/unifiedorder");
            //client设置参数
            client.setXmlParam(WXPayUtil.generateSignedXml(paramMap, wxProperties.getPartnerkey()));
            client.setHttps(true);
            client.post();

            String content = client.getContent();
            Map<String, String> contentMap = WXPayUtil.xmlToMap(content);
            log.info("微信支付返回数据:{}", contentMap);
            //封装结果
            Map<String, Object> result = new HashMap<>();
            result.put("orderId", orderId);
            result.put("totalFee", order.getAmount());
            result.put("resultCode", contentMap.get("result_code"));
            //获取支付二维码地址
            result.put("codeUrl", contentMap.get("code_url"));
            //设置支付时间为两小时
            redisTemplate.opsForValue().set(orderId, result, 2, TimeUnit.HOURS);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public Map<String, String> getPayStatus(String orderId) {
        if (StringUtils.isEmpty(orderId)) {
            throw new HospitalException(ResultCodeEnum.PARAM_EMPTY);
        }
        try {
            //根据order id 获取订单信息
            OrderInfo orderInfo = orderService.getById(orderId);
            if (orderInfo == null) {
                throw new HospitalException(ResultCodeEnum.NO_SUCH_DATA);
            }
            //设置请求参数
            Map<String, String> requestParam = new HashMap<>();
            requestParam.put("appid", wxProperties.getAppid());
            requestParam.put("mch_id", wxProperties.getPartner());
            requestParam.put("out_trade_no", orderInfo.getOutTradeNo());
            requestParam.put("nonce_str", WXPayUtil.generateNonceStr());
            HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/orderquery");
            client.setXmlParam(WXPayUtil.generateSignedXml(requestParam, wxProperties.getPartnerkey()));
            client.setHttps(true);
            //发送请求
            client.post();
            //获取响应数据
            String xmlContent = client.getContent();
            Map<String, String> resultMap = WXPayUtil.xmlToMap(xmlContent);
            log.info("接受到微信支付响应：{}", resultMap);
            return resultMap;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean refund(Long orderId) {
        try {


            //根据订单id 和 支付方式获取支付信息
            PaymentInfo paymentInfo = paymentService.getInfoByOrderId(String.valueOf(orderId), PaymentTypeEnum.WEIXIN.getStatus());

            //保存退款订单信息
            RefundInfo refundInfo = refundInfoService.saveRefundInfo(paymentInfo);
            //判断该订单是否已经退款
            if (refundInfo.getRefundStatus().intValue() == RefundStatusEnum.REFUND.getStatus().intValue()) {
                return true;
            }
            Map<String, String> paramMap = new HashMap<>(8);
            //公众账号ID
            paramMap.put("appid", wxProperties.getAppid());
            //商户编号
            paramMap.put("mch_id", wxProperties.getPartner());
            paramMap.put("nonce_str", WXPayUtil.generateNonceStr());
            //微信订单号
            paramMap.put("transaction_id", paymentInfo.getTradeNo());
            //商户订单编号
            paramMap.put("out_trade_no", paymentInfo.getOutTradeNo());
            //商户退款单号
            paramMap.put("out_refund_no", "tk" + paymentInfo.getOutTradeNo());
//       paramMap.put("total_fee",paymentInfo.getTotalAmount().multiply(new BigDecimal("100")).longValue()+"");
//       paramMap.put("refund_fee",paymentInfo.getTotalAmount().multiply(new BigDecimal("100")).longValue()+"");
            paramMap.put("total_fee", "1");
            paramMap.put("refund_fee", "1");
            String xmlParam = WXPayUtil.generateSignedXml(paramMap, wxProperties.getPartnerkey());
            HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/secapi/pay/refund");
            client.setXmlParam(xmlParam);
            client.setHttps(true);
            client.setCert(true);
            client.setCertPassword(wxProperties.getPartner());
            client.post();

            //获取响应参数
            String xmlContent = client.getContent();
            //转为Map
            Map<String, String> mapContent = WXPayUtil.xmlToMap(xmlContent);
            if (MapUtils.isNotEmpty(mapContent) && WXPayConstants.SUCCESS.equalsIgnoreCase(mapContent.get("result_code"))){
                refundInfo.setCallbackTime(new Date());
                refundInfo.setOutTradeNo(mapContent.get("refund_id"));
                refundInfo.setRefundStatus(RefundStatusEnum.REFUND.getStatus());
                refundInfoService.updateById(refundInfo);
                return true;
            }
            return false;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }

    }
}
